 package it.softphone.cmc.server.service.impl;

 import it.softphone.cmc.server.config.GAEConfig;
import it.softphone.cmc.server.entity.AccountEntity;
import it.softphone.cmc.server.entity.AccountPreferenceEntity;
import it.softphone.cmc.server.entity.TenantEntity;
import it.softphone.cmc.server.entity.UserEntity;
import it.softphone.cmc.server.entity.VersionEntity;
import it.softphone.cmc.server.service.QueryTask;
import it.softphone.cmc.server.service.SystemManagerService;
import it.softphone.cmc.server.service.TenantService;
import it.softphone.cmc.server.service.exception.CMCDuplicateEntityException;
import it.softphone.cmc.server.service.exception.CMCException;
import it.softphone.cmc.server.service.json.JSONAutoBeanAttributesConverter;
import it.softphone.cmc.server.service.json.JSONAutoBeanDateConverter;
import it.softphone.cmc.server.service.json.JSONBlobConverter;
import it.softphone.cmc.server.service.json.JSONKeyConverter;
import it.softphone.cmc.shared.entity.Account;
import it.softphone.cmc.shared.entity.Account.Preference;
import it.softphone.cmc.shared.entity.Attributes;
import it.softphone.cmc.shared.entity.Tenant;
import it.softphone.cmc.shared.entity.TenantUser;
import it.softphone.cmc.shared.entity.Version;
import it.softphone.commons.exception.NullParameterException;
import it.softphone.commons.json.server.JSONException;
import it.softphone.commons.json.server.JSONObject;
import it.softphone.commons.json.server.JSONUtils;
import it.softphone.commons.util.Util;

import java.util.List;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.datanucleus.api.jpa.NucleusJPAHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import biz.source_code.miniTemplator.MiniTemplator;

import com.google.appengine.api.datastore.Blob;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

/**
 * 
 * @author softphone
 *
 */
@Service
public class GAESystemManagerServiceImpl extends GAEEntityManagerServiceImpl implements SystemManagerService {

	//private static final String REGISTRATION_CACHE = "registrationCache";

	private static final String MASTER_MAIL = "soulsoftware2010@gmail.com";

	@Autowired GAEConfig serviceConfig;
	
	/**
	 * 
	 */
	protected void assertIsSystemTenant( ) {
		final TenantService service = serviceConfig.tenantService();
		
		final String currentTenant  = service.getCurrentTenant();
		if( currentTenant !=null  && !service.getSystemTenant().equals(currentTenant) ) 
			throw new IllegalStateException( String.format("current is not SYSTEM tenent but [%s]", currentTenant));

	}

	
	/**
	 * 
	 * @param defaultVersion
	 * @return
	 * @throws CMCException
	 */
	public Version getLastVersion( final String defaultVersion ) throws CMCException {
		VersionEntity lastVersion = execNewTransaction( "getLastVersion", new EMTask<VersionEntity>() {
			
			@Override
			public VersionEntity exec(EntityManager em) throws Exception {
				
				VersionEntity lastVersion = getLastVersionInTransaction(em);
	
				if (lastVersion == null) {

					logger.warn( String.format("no previous version detected! set the last version [%s]", defaultVersion) );
					
					lastVersion = new VersionEntity();
	
					lastVersion.setVersion(defaultVersion);
	
					em.persist(lastVersion);
					
					return null;
				}
				
				return lastVersion;
			}});

		return lastVersion;
	}
	
	/**
	 * 
	 * @param em
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private VersionEntity getLastVersionInTransaction( EntityManager em) {
		
		Query findAll = em.createQuery(  "SELECT v FROM VersionEntity v ORDER BY v.releaseDate DESC");
		
		List<VersionEntity> result = findAll.getResultList();
		
		if( result == null || result.isEmpty() ) return null;
		
		return result.get(0);
				

	}
	
		
	/**
	 * 
	 */
	@PostConstruct
	public void init() {
		JSONUtils.registerConverter( Key.class, new JSONKeyConverter() );		
		JSONUtils.registerConverter( java.util.Date.class, new JSONAutoBeanDateConverter() );
		JSONUtils.registerConverter( Blob.class, new JSONBlobConverter() );
		JSONUtils.registerConverter( Attributes.class, new JSONAutoBeanAttributesConverter() );
	}
	
	/**
	 * 
	 */
	@Override
	public boolean  createTenant(final Tenant tenant) throws CMCException {
		if( tenant==null ) throw new NullParameterException("tenant");


		return execNewTransaction( "createTenant", new EMTask<Boolean>() {

			@Override
			public Boolean exec(EntityManager em) throws Exception {
				
				Tenant prevTenant = findTenantById( "name", tenant.getName() );

				if( prevTenant == null ) {
					em.persist(tenant);
					em.refresh(tenant);
					
					return Boolean.TRUE;
				}
				return Boolean.FALSE;
			}
			
		});
		
	}
	
	@Override
	public void storeTenant( final Tenant tenant) throws CMCException {
		if( tenant==null ) throw new NullParameterException("tenant");

		execNewTransaction( "storeTenant", new EMTask<Void>() {

			@Override
			public Void exec(EntityManager em) throws Exception {
				em.persist(tenant);
				return null;
			}
			
		});

		
	}

	@Override
	public void deleteTenant( final Tenant tenant) throws CMCException {
		if( tenant==null ) throw new NullParameterException("tenant");
		if( !(tenant instanceof TenantEntity )) throw new IllegalArgumentException("tenant is not instance of TenantEntity");
		
		execNewTransaction( "deleteTenant", new EMTask<Void>() {

			@Override
			public Void exec(EntityManager em) throws Exception {
				TenantEntity t = em.find(TenantEntity.class, ((TenantEntity)tenant).getGUID());
				
				em.remove(t);

				return null;
			}
			
		});
		
	}

	private void validateTenantIds( String nameOfId ) throws CMCException {

		
		final String [] ids = { "name", "id1", "id2", "id2" };
		
		for( String s : ids ) {
			if( s.equals(nameOfId) ) return ;
		}

		
		throw new CMCException( "nameOfId is not valid field name! it must be " + ids);
	}
	
	/**
	 * 
	 */
	@Override
	public Tenant findSystemTenant() throws CMCException {
		return exec( "findSystemTenant", new EMTask<Tenant>() {

			@Override
			public Tenant exec(EntityManager em) throws Exception {

				Tenant result = null;
				
				Query query = em.createQuery( String.format("SELECT r FROM TenantEntity r WHERE r.name = ''"));
				
				result = (Tenant) query.getSingleResult();
			
				return result;
				
			}
		});
	}


	/**
	 * 
	 */
	@Override
	public Tenant findTenantById( final String nameOfId, final String valueOfId ) throws CMCException  {
		if( nameOfId==null ) throw new NullParameterException("nameOfId");
		if( valueOfId==null ) throw new NullParameterException("valueOfId");

		final String method = "findTenantById";

		validateTenantIds(nameOfId);
		
		return exec( method, new EMTask<Tenant>() {

				@Override
				public Tenant exec(EntityManager em) throws Exception {

					Tenant result = null;
					
					Query query = em.createQuery( String.format("SELECT r FROM TenantEntity r WHERE r.%s = :value", nameOfId));
					
					query.setParameter("value", valueOfId);
					
					result = (Tenant) query.getSingleResult();
				
					return result;
					
				}
		});
	}
	
	/**
	 * 
	 */
	@Override
	@SuppressWarnings("unchecked")
	public void findAllTenants( final QueryTask<List<Tenant>> task) throws CMCException {
		if( task==null ) throw new NullParameterException("task");

		exec( "findAllTenants", new EMTask<Void>() {

			@Override
			public Void exec(EntityManager em) throws Exception {
				java.util.List<Tenant> result = null;

				Query query = em.createQuery( "SELECT r FROM TenantEntity r ");
				
				result = query.getResultList();
				
				task.run(result);
				return null;
			}
		});
			

	}

	@Override
	public Tenant makeTenantFromJson(JSONObject object) throws JSONException {
		Tenant result = makeTenant();	
		
		JSONUtils.copyTo(result, object);

		return result;
	}

	@Override
	public Tenant makeTenant() {
		return new TenantEntity();
	}

	
	///////////////////////////////////////////////////////
	///
	/// AAA
	///
	///////////////////////////////////////////////////////
	@Override
	public TenantUser makeUser() {
		return new UserEntity();
	}

	@Override
	public TenantUser makeUserFromJson(JSONObject object) throws JSONException {
		TenantUser result = makeUser();
		
		JSONUtils.copyTo(result, object);

		return result;
	}

	
	@Override
	public Account makeAccount()   {
		
		AccountEntity result = new AccountEntity();
			
		return result;
	}
	
	@Override
	public Account makeAccountFromJson(it.softphone.commons.json.server.JSONObject object) throws JSONException  {
		
		Account result = makeAccount();
			
		JSONUtils.copyTo(result, object);
		
		return result;
	}
	
	/**
	 * 
	 * @param user
	 * 
	 */
	public void storeUser(final TenantUser user, final boolean enable) throws CMCException {
		if( ! (user instanceof UserEntity) ) throw new IllegalStateException("user is not an entity!");
		if( user.getUID()==null) throw new IllegalStateException("UID has not been provided!");
		
		exec("storeUser", new EMTask<Void>() {

			@Override
			public Void exec(EntityManager em) throws Exception {
				
				((UserEntity)user).setEnabled(enable);
				em.persist(user);
				
				return null;
			}
			
		});
	}

	/**
	 * 
	 * @param user
	 * 
	 */
	public void storeAccount(final Account account) throws CMCException {
		if( account.getUID()==null) throw new IllegalStateException("UID has not been provided!");
		
		if( NucleusJPAHelper.isPersistent(account) ) return;

		execTransaction("storeAccount", new EMTask<Void>() {

			@Override
			public Void exec(EntityManager em) throws Exception {
				
				em.persist(account);
				
				return null;
			}
			
		});
	}

	/**
	 * 
	 * @param task
	 */
	@SuppressWarnings("unchecked")
	public void findAllUsers( final QueryTask<List<TenantUser>> task) {
		if( task==null ) throw new NullParameterException("task");
		
		try {
			exec( "findAllUsers", new EMTask<Void>() {

				@Override
				public Void exec(EntityManager em) throws Exception {
					Query query = em.createQuery( "SELECT r FROM UserEntity r ");
					
					java.util.List<TenantUser> result  = query.getResultList();
					
					task.run(result);
					
					return  null;
				}
				
			});
		} catch (CMCException e) {
			// TODO LOG
		}

		
	}
	
	/**
	 * 
	 * @param entityManager
	 * @param userName
	 * @return
	 * @throws CMCException 
	 */
	@Override
	public Account findAccountById( final String id) throws CMCException {
		if( id==null ) throw new NullParameterException("id");
		
		assertIsSystemTenant();
		
		final Account account = exec("findAccountById", new EMTask<Account>() {

			@Override
			public Account exec(EntityManager em)  throws Exception {
				AccountEntity result = null;
				
				Query query = em.createQuery( "SELECT r FROM AccountEntity r WHERE r.id = :userId ");
				
				query.setParameter("userId", id);
				
				result = (AccountEntity) query.getSingleResult();
						
				return result;
			}
			
		});
		
		
		return account;
	}
	
		
	/**
	 * 
	 * @param id
	 * @return
	 * @throws CMCException
	 */
	@Override
	public TenantUser findUserByAccount( final Account account ) throws CMCException {

		TenantUser result = null;
			result = exec( "findUserByAccount" , new EMTask<TenantUser>() {

				@Override
				public TenantUser exec(EntityManager em) throws Exception {
					Query query = em.createQuery( "SELECT r FROM UserEntity r WHERE r.accountUID = :k_key ");
					
					query.setParameter("k_key", account.getUID());
					return (TenantUser) query.getSingleResult();
				}
				
			});

		
		return result;
	}

	
	/**
	 * 
	 */
	@SuppressWarnings("unchecked")
	@Override
	public String requestAccountCreation(String accountId, String password, String deviceId, String requestUrl ) throws CMCException {
		if( accountId == null ) throw new NullParameterException("accountId");
		if( password == null ) throw new NullParameterException("password");
		
		//
		// ASSUMPTION - ACCOUNTID MUST BE A VALID MAIL
		//
		if( !Util.isValidMail(accountId) ) 
			throw new IllegalArgumentException( String.format("accountId [%s] is no a valid mail!", accountId));
		
		//
		// Check if account exist
		//
		Account prevAccount = findAccountById( accountId );	
		if( prevAccount != null ) {
			throw new CMCException(String.format("account [%s] already exist!", accountId));
		}

		java.util.UUID requestToken = java.util.UUID.randomUUID();
		
		AccountEntity account = (AccountEntity) makeAccount();
		
		account.setId(accountId);
		account.setPasswd( password );
		account.setDeviceId(deviceId);
		account.setMail(deviceId);
		
		/*
		java.util.Set<String> roles = new java.util.HashSet<String>(2);
		roles.add( Roles.B2B );
		roles.add( Roles.GUEST );
		
		account.setRoles(roles);
		*/
		
		//final javax.cache.Cache registrationCache = ApplicationConfig.getBean(javax.cache.Cache.class, REGISTRATION_CACHE);

		//final javax.mail.Session mailSession = ApplicationConfig.getBean(javax.mail.Session.class);
		
		final String registerLink = new StringBuilder()
											.append(requestUrl)
											.append("auth")
											.append("/register/2?requestToken=")
											.append(requestToken.toString())
											.toString();
		
        javax.mail.Message msg = new javax.mail.internet.MimeMessage(serviceConfig.mailService());
        
        try {
			msg.setFrom(new javax.mail.internet.InternetAddress(MASTER_MAIL, "Admin"));
	        msg.addRecipient(javax.mail.Message.RecipientType.TO,
                    new javax.mail.internet.InternetAddress(accountId, "New User"));
	        msg.setSubject("CMC Account activation");
	      
	        String htmlBody = null;
	        
	        java.net.URL resourceUrl = getClass().getClassLoader().getResource("confirmRegistrationMailBody.html");
	        
	        if( resourceUrl == null ) {
	        	logger.warn("resource 'confirmRegistrationMailBody.html' doesn't exist!. Default used");
		        
	        	htmlBody = new StringBuilder()
				.append( "<A href='")
				.append(registerLink)
				.append("' >")
				.append("click the following link to enable your CMC account")
				.append("</A>")
				.toString();
	        }
	        else {
	        	
		        MiniTemplator t = new MiniTemplator(resourceUrl);
	        
		        java.net.URI uri = new java.net.URI(registerLink);
		        
		        t.setVariable("cmc.registerLink", registerLink);
		        t.setVariable("cmc.mobileRegisterLink", registerLink.replaceFirst(uri.getScheme(), "cmc"), true);
		        
		        htmlBody = t.generateOutput();
		        
		        logger.debug( "message body\n".concat(htmlBody) );
	        }
	        
	        javax.mail.Multipart mp = new javax.mail.internet.MimeMultipart();

	        javax.mail.internet.MimeBodyPart htmlPart = new javax.mail.internet.MimeBodyPart();
	        htmlPart.setContent(htmlBody, "text/html");
	        mp.addBodyPart(htmlPart);

	        msg.setContent(mp);        

	        javax.mail.Transport.send(msg);
		} catch (Exception e) {
			
			final String message = "error sending confirmation mail";
			throw new CMCException( message, e);
			
		}
        
		serviceConfig.registrationCache().put( requestToken, account);
		
     	logger.debug(registerLink);
     	
		return requestToken.toString();
	}

	/**
	 * @param requestToken
	 */
	@Override
	public Account confirmAccountCreation(String requestToken) 	throws CMCException {
		if( requestToken == null ) throw new NullParameterException("requestToken");

		java.util.UUID uuid = java.util.UUID.fromString(requestToken);

		AccountEntity account = (AccountEntity) serviceConfig.registrationCache().get(uuid);
		
		if( account == null ) {
			throw new CMCException( "invalid account confirmation request!" );
		}

		serviceConfig.registrationCache().remove(uuid);
		
		return createAccount(account, null /* password is already set*/, true);
	}

	/**
	 * This method must be called within system tenant
	 * 
	 * @param account
	 * @param user
	 * @param activeRegistration
	 * @return
	 * @throws CMCException
	 */
	public Account createAccount( final Account account, final String password, final boolean activeRegistration ) throws CMCException  {
		if( account == null ) throw new NullParameterException("account");
		if( !(account instanceof AccountEntity)) throw new IllegalArgumentException("account is not an instance of AccountEntity class!");
		
		//
		// Create Account in system tenant
		//
		
		execNewTransaction("createAccount", new EMTask<Account>() {

				@Override
				public Account exec(EntityManager em) throws Exception {
					
					AccountEntity account2 = (AccountEntity) account;
					if( password!=null ) account2.setPasswd(password);
					if( activeRegistration) account2.register();
					
					return createAccountInTransaction( em, account2);
				}
				
		});
		
		
		
		return account;
	}
	
	/**
	 * This method must be called within system tenant
	 * 
	 * @param account
	 * @param user
	 * @param activeRegistration
	 * @return
	 * @throws CMCException
	 */
	private AccountEntity createAccountInTransaction( final EntityManager em, final AccountEntity account ) throws CMCException  {
		if( em == null ) throw new NullParameterException("em");
		
		
		if( account.getId() == null ) throw new IllegalStateException("account.id must not be null");
		if( account.getUID() != null ) throw new IllegalStateException("account.UID must be null");
		if( account.getPasswd() == null ) throw new IllegalStateException("account.password must not be null");

		
		//
		// Check if account exist
		//
		Account prevAccount = findAccountById( account.getId() );	
		if( prevAccount != null ) {
			throw new CMCDuplicateEntityException(String.format("account [%s] already exist!", account.getId()));
		}
		
		em.persist(account);	
		em.refresh(account);
		
		return account;
	}

	/**
	 * 
	 */
	@Override
	public TenantUser createUser( final TenantUser user, final Account account, final boolean enable )	throws CMCException {
		
		return execNewTransaction("createUser", new EMTask<TenantUser>() {

			@Override
			public TenantUser exec(EntityManager em) throws Exception {
				return createUserInTransaction( em, user, account, enable );
			}
			
		});
		
	}

	/**
	 * 
	 * @param id
	 * @return
	 * @throws CMCException
	 */
	private TenantUser findUserByNetworkId( final String networkId /*, final String userName*/) throws CMCException {

		TenantUser result = null;
			result = exec( "findUserByNetworkId" , new EMTask<TenantUser>() {

				@Override
				public TenantUser exec(EntityManager em) throws Exception {
					Query query = em.createQuery( "SELECT r FROM UserEntity r WHERE r.id = :networkId ");
					
					query.setParameter("networkId", networkId);
					return (TenantUser) query.getSingleResult();
				}
				
			});

		
		return result;
	}
	
	/**
	 * 
	 * @param platformId
	 * @return
	 * @throws CMCException
	 */
	private TenantUser findUserByUID( final String platformId) throws CMCException {

		TenantUser result = null;
			result = exec( "findUserByUID" , new EMTask<TenantUser>() {

				@Override
				public TenantUser exec(EntityManager em) throws Exception {
					Query query = em.createQuery( "SELECT r FROM UserEntity r WHERE r.UID = :platformId ");
					
					query.setParameter("platformId", platformId);
					return (TenantUser) query.getSingleResult();
				}
				
			});

		
		return result;
	}
	
	
	/**
	 * 
	 */
	protected TenantUser createUserInTransaction( final EntityManager em, final TenantUser user, final Account account, final boolean enable)	throws CMCException {
		if( em == null ) throw new NullParameterException("em");
		
		
		if( user == null ) throw new NullParameterException("user");
		if( !(user instanceof UserEntity) ) throw new NullParameterException("user is not an entity!");
		if( user.getUID() != null ) throw new IllegalStateException("user.UID must be null");
		if( user.getTenant() == null ) throw new NullParameterException("user.tenant must not be null");

		// Account could be null unless the abilitation is required
		if( enable ) { 
			if( account == null ) throw new NullParameterException("account");
			if( account.getUID() == null ) throw new IllegalStateException("require user activation but account.UID is null!");
		}
		
		// Account could be null unless the abilitation is required
		if( account==null ) {			
			if( user.getId() == null ) throw new IllegalStateException("user.id must not be null");
			if( user.getName() == null ) throw new NullParameterException("user.name must not be null");
		}
		
		//
		// Check if tenant exist
		//
		final Tenant t =  serviceConfig.tenantService().execAsSystemTenant( new TenantService.Task<Tenant>() {

			@Override
			public Tenant exec() throws Exception {
				return findTenantById( "name", user.getTenant());
			}
		});
		
		if( t == null ) {
			throw new IllegalArgumentException(String.format("tenant [%s] does't exist!", user.getTenant()));
		}

		//
		// Check if user already Exist
		//
		if( account != null ) {
			
			TenantUser prevUser = findUserByAccount(account);
			if( prevUser != null ) {
				throw new CMCDuplicateEntityException(String.format("Already exists an user [%s] joined with account [%s] in tenant [%s]!", prevUser, account, user.getTenant()));
			}
		}
		else {
			TenantUser prevUser = findUserByNetworkId(user.getId());
			if( prevUser != null ) {
				throw new CMCDuplicateEntityException(String.format("Already exists an user [%s] in tenant [%s]!", prevUser, user.getTenant()));
			}
			
		}
		
		//
		// Create User into target tenant
		//
		((UserEntity)user).setEnabled(enable);				
		
		if( account!= null ) user.setAccountUID( account.getUID() );
		
		em.persist(user);	
		em.refresh(user);

		if( account != null ) account.setUser(user);

		return null;
	}

	/**
	 * 	 
	 * This method must be called within system tenant
	 *
	 */
	@Override
	public void activeRegistration( final Account account) throws CMCException {
		if( account==null ) throw new NullParameterException("account");
		if( account.getUID()==null ) throw new NullParameterException("account.UID");
		
		final AccountEntity entity = exec("findAccountByKey", new EMTask<AccountEntity>() {

				@Override
				public AccountEntity exec(EntityManager em)  throws Exception {
					AccountEntity result = em.find(AccountEntity.class, KeyFactory.stringToKey(account.getUID()));
					return result;
				}
				
		});

		entity.register();

		exec("storeAccount", new EMTask<Void>() {

			@Override
			public Void exec(EntityManager em) throws Exception {
				
				em.persist(entity);
				
				return null;
			}
			
		});	
		
	}

	/**
	 * 
	 */
	@Override
	public boolean isRegistrationActive( final Account account) {
		if( account==null ) throw new NullParameterException("account");
		if( account.getUID()==null ) throw new NullParameterException("account.UID");
		
		AccountEntity entity;
		try {
			entity = exec("findAccountByKey", new EMTask<AccountEntity>() {

				@Override
				public AccountEntity exec(EntityManager em)  throws Exception {
					AccountEntity result = em.find(AccountEntity.class, KeyFactory.stringToKey(account.getUID()));
					return result;
				}
				
			});

			
		} catch (CMCException e) {
			logger.warn( String.format("error find account.UID=[%s]", account.getUID()), e );
			return false;
		}

		return ( entity != null ) ? entity.isRegistered() : false;

	}

	@Override
	public boolean isRequestTokenExpired(String registrationToken) {
		return false;
	}

	
	@Override
	public Preference makeAccountPreference() {
		return new AccountPreferenceEntity();
	}


	@Override
	public void findAccountPreferences(Account account,	final QueryTask<List<Preference>> task) throws CMCException {
		if( account == null ) throw new NullParameterException("account");
		if( account.getUID() == null ) throw new NullParameterException("account.UID");
		if (!( account instanceof AccountEntity )) throw new IllegalArgumentException("account is not right type");
		
		final Key accountKey = ((AccountEntity)account).getGUID();
		
		exec( "findAccountPreferences", new EMTask<Void>() {

			@SuppressWarnings("unchecked")
			@Override
			public Void exec(EntityManager em) throws Exception {
				
				Query query = em.createQuery( "select from AccountPreferenceEntity s where accountRef = :1 ");
				query.setParameter(1, accountKey);
				
				List<Account.Preference> result = query.getResultList();
				
				for( Account.Preference e : result  ) {
					((AccountPreferenceEntity)e).postLoad();
				}
				
				task.run( result ) ;
				
				return null;
			}
		});
	}


	@Override
	public void createAccountPreference( final Account account, final Preference preference) throws CMCException {
		if( account == null ) throw new NullParameterException("account");
		if( preference == null ) throw new NullParameterException("preference");
		if( preference.getTenant() == null ) throw new NullParameterException("preference.tenant");
		
		if( !( account instanceof AccountEntity ) ) 							
			throw new IllegalArgumentException("account is not an entity!");
		if( !( preference instanceof AccountPreferenceEntity ) )	
			throw new IllegalArgumentException("preference is not an entity!");
		if( !( preference.getTenant() instanceof TenantEntity ) )	
			throw new IllegalArgumentException("preference.tenant is not an entity!");
		
		execNewTransaction( "createAccountPreference", new EMTask<Void>() {

			@Override
			public Void exec(EntityManager em) throws Exception {
				
				final AccountEntity accountE = (AccountEntity) account;

				final TenantEntity tenantE = (TenantEntity)preference.getTenant();
				
				AccountPreferenceEntity p1 = (AccountPreferenceEntity) preference;
				p1.setAccountRef( accountE.getGUID() );
				p1.setTenantRef( tenantE.getGUID() );
								
				em.persist( p1 );
				
				return null;
			}
		});
		
	}


	@Override
	public void deleteAccountPreferenceByTenant(final Account account, final Tenant tenant) throws CMCException{
		if( account == null ) throw new NullParameterException("account");
		if( tenant == null ) throw new NullParameterException("tenent");
		if( !( account instanceof AccountEntity ) ) 							
			throw new IllegalArgumentException("account is not an entity!");
		if( !( tenant instanceof TenantEntity ) )	
			throw new IllegalArgumentException("preference.tenant is not an entity!");

		execNewTransaction( "deleteAccountPreference", new EMTask<Void>() {

			@Override
			public Void exec(EntityManager em) throws Exception {

				Query query = em.createQuery( "SELECT r FROM AccountPreferenceEntity r WHERE r.tenantRef = :1 ");
				
				query.setParameter(1, ((TenantEntity)tenant).getGUID());
				
				AccountPreferenceEntity result = (AccountPreferenceEntity) query.getSingleResult();
				
				em.remove(result);
				
				return null;
			}
		});
	}


}